/* Copyright (C) 2015-2016 HangZhou Zenzet Technology Co., Ltd.
 * All right reserved

 * File:crypto/locketdigest.c
 * Author:guojianchuan/max
 * Date:2016-06-02

 */

/* system header */
#include <string.h>

/* 3rd project header */
#include "openssl/evp.h"

/* my project header */
#include "locketdef.h"
#include "locketerrno.h"
#include "locketlog.h"
#include "locketcommon.h"
#include "locketconvert.h"

#include "locketdigest.h"

/* HASH最大长度 */
#define LOCKET_HASH_MAX_LEN   1024

/**
* @Function:LOCKET_CIPHER_CheckDigestParam
* @Author: guojianchuan/max
* @Date: 2016-06-27
* @Description: Check digest param
* @caution: 
*/
static int LOCKET_CIPHER_CheckDigestParam(IN CIPHER_HASH_S *pstHash)
{
    if (NULL == pstHash)
    {
        return ERR_COMMON_INVALID_PARAM;
    }

    if ((NULL == pstHash->pucInput) ||
        (NULL == pstHash->pucOutput) ||
        ((0 == pstHash->iInputLen) && (ENCODE_BINARY != pstHash->iInputEncode)))
    {
        return ERR_COMMON_INVALID_PARAM;
    }

    if (ERR_COMMON_SUCCESS != LOCKET_CIPHER_CheckEncode (pstHash->iInputEncode))
    {
        return ERR_COMMON_INVALID_PARAM;
    }

    if (ERR_COMMON_SUCCESS != LOCKET_CIPHER_CheckEncode (pstHash->iOutputEncode))
    {
        return ERR_COMMON_INVALID_PARAM;
    }

    return ERR_COMMON_SUCCESS;
}

/**
* @Function: LOCKET_CIPHER_Digest
* @Author: guojianchuan/max
* @Date: 2016-06-07
* @Description: Hash sign (eg:MD5, SHA1, SHA256, SHA512)
* @caution:
*/
int LOCKET_CIPHER_Digest(INOUT CIPHER_HASH_S *pstHash)
{
    int iErr = ERR_COMMON_SUCCESS;

    /* Check param */
    iErr = LOCKET_CIPHER_CheckDigestParam (pstHash);
    if (ERR_COMMON_SUCCESS != iErr)
    {
        return iErr;
    }

    /* Get algo */
    const EVP_MD *pstMd = NULL;
    pstMd = EVP_get_digestbyname(pstHash->szAlgoName);
    if (ERR_COMMON_SUCCESS != iErr)
    {
        iErr = ERR_COMMON_INVALID_ALGO;
        LOCKET_LOGERROR ("EVP_get_cipherbyname failed. err:%lu %s", iErr, LOCKET_ERR_GetString());
        return iErr;
    }

    /* Convert input */
    unsigned char *pucConvertInput   = NULL;
    unsigned char *pRet              = NULL;
    int iConvertInputLen             = 0;
    if (ENCODE_BINARY != pstHash->iInputEncode)
    {
        pucConvertInput= (unsigned char*) LOCKET_malloc (pstHash->iInputLen);
        if (NULL == pucConvertInput)
        {
            iErr = ERR_COMMON_NO_MEM;
            return iErr;
        }
        memset (pucConvertInput, 0, pstHash->iInputLen);

        pRet = LOCKET_CIPHER_OtherToByte (pstHash->iInputEncode, pstHash->pucInput,
                                           pstHash->iInputLen, pucConvertInput, &iConvertInputLen);
        if (NULL == pRet)
        {
            LOCKET_free ((void**) &pucConvertInput);
            iErr = ERR_COMMON_CONVERT_FAILED;
            return iErr; 
        }
    }
    else
    {
        pucConvertInput   = pstHash->pucInput;
        iConvertInputLen = pstHash->iInputLen;
    }

    EVP_MD_CTX *pstDigestCtx = NULL;
    unsigned char szConvertOutput[LOCKET_HASH_MAX_LEN] = {0};
    int iOutputLen                                     = 0;
    do
    {
        /* Init digest */
        pstDigestCtx = EVP_MD_CTX_new ();
        if (NULL == pstDigestCtx)
        {
            iErr = ERR_COMMON_NO_MEM;
            break;
        }

        iErr = EVP_DigestInit_ex(pstDigestCtx,pstMd, NULL);
        if (ERR_COMMON_SUCCESS != iErr)
        {
            iErr = ERR_COMMON_OPENSSL_FAILED;
            LOCKET_LOGERROR ("EVP_DigestInit failed. err:%lu %s", iErr, LOCKET_ERR_GetString());
			break;
        }

        /* Update digest */
        iErr = EVP_DigestUpdate(pstDigestCtx, pucConvertInput, iConvertInputLen);
        if (ERR_COMMON_SUCCESS != iErr)
        {
            iErr = ERR_COMMON_OPENSSL_FAILED;
            LOCKET_LOGERROR ("EVP_DigestUpdate failed. err:%lu %s", iErr, LOCKET_ERR_GetString());
			break;
        }

        /* Output digest */
        iErr = EVP_DigestFinal_ex(pstDigestCtx, szConvertOutput, (unsigned int*) &iOutputLen);
        if (ERR_COMMON_SUCCESS != iErr)
        {
            iErr = ERR_COMMON_OPENSSL_FAILED;
            LOCKET_LOGERROR ("EVP_DigestFinal failed. err:%lu %s", iErr, LOCKET_ERR_GetString());
			break;
        }

        if (ENCODE_BINARY != pstHash->iOutputEncode)
        {
            pRet = LOCKET_CIPHER_ByteToOther(pstHash->iOutputEncode, szConvertOutput, iOutputLen, pstHash->pucOutput, &pstHash->iOutputLen);
            if (NULL == pRet)
            {
                iErr = ERR_COMMON_CONVERT_FAILED;
                break;
            }
        }
        else
        {
            memcpy (pstHash->pucOutput, szConvertOutput, iOutputLen);
            pstHash->iOutputLen = iOutputLen;
        }

    } while (0);

    if (ENCODE_BINARY != pstHash->iInputEncode)
    {
        LOCKET_free ((void**) &pucConvertInput);
    }
    EVP_MD_CTX_free(pstDigestCtx);

    if (ERR_COMMON_SUCCESS != iErr)
    {
        return iErr;
    }

    return ERR_COMMON_SUCCESS;
}
